/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <mc/dict.h>
#include <mc/log.h>
#include <mc/variant.h>
#include <mc/filesystem.h>
#include <ncsi/ncsi_huawei.h>
#include <ncsi/ncsi_protocol.h>
#include <ctime>
#include <sys/stat.h>

#include "ncsi_over_mctp_huawei.h"

namespace dev {

response_error_code_t respense_error_code = // 错误码定义
    {{0x1,
      {
          {0x8002, "invalid OEM CMD head info"},
          {0x8003, "OEM get info failed"},
          {0x8004, "OEM error code over maximum"},
          {0x8005, "OEM get driver info failed"},
          {0x8006, "OEM cable type not support"},
          {0x8007, "OEM cable type undefined"},
          {0x8008, "OEM optical module not exist"},
          {0x8009, "OEM enable netcard LLDP failed"},
          {0x9000, "OEM get controller stats failed"},
      }},
     {0x3, {{0x7FFFF, "unsupport command"}}}};

bool check_respense_data(oem_huawei_public_rsp_t* rsp, uint8_t oem_sub_command_id) {
    uint16_t response_code = SHORT_BY_BIG_ENDIAN(rsp->response_code);
    uint16_t reason_code   = SHORT_BY_BIG_ENDIAN(rsp->reason_code);
    if (response_code != 0x0000) {
        if (respense_error_code.find(response_code) != respense_error_code.end()) {
            error_code_map_t error_code_map = respense_error_code[response_code];
            if (error_code_map.find(reason_code) != error_code_map.end()) {
                dlog("unsuccessful request with respense code: ${response_code}, reason code: "
                     "${reason_code}",
                     ("response_code", response_code)("reason_code", error_code_map[reason_code]));
                return false;
            }
        }
        dlog("unsuccessful request with respense code: ${response_code}, reason code: "
             "${reason_code}",
             ("response_code", response_code)("reason_code", reason_code));
        return false;
    }
    if (rsp->manufacturer_id != HUAWEI_MANUFACTURE_ID) {
        dlog("invalid response data with wrong manufacturer id: ${manufacturer_id}",
             ("manufacturer_id", rsp->manufacturer_id));
        return false;
    }
    if (rsp->sub_command_id != oem_sub_command_id) {
        dlog("invalid response data with wrong sub command id: ${sub_command_id}",
             ("sub_command_id", rsp->sub_command_id));
        return false;
    }
    return true;
}

mc::dict get_oem_command_ctx(uint8_t package_id, uint8_t channel_id) {
    mc::dict ctx({{"PacketType", OEM_COMMAND},
                  {"ExpectRspPacketType", OEM_COMMAND_RSP},
                  {"ChannelId", channel_id},
                  {"PackageId", package_id}});
    return ctx;
}

bool ncsi_over_mctp_huawei::get_bdf(uint8_t package_id, uint8_t channel_id,
                                    ncsi_bdf_info_t* bdf_info) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_GET_BDF, 0x00);

    oem_huawei_bdf_info_rsp_t* rsp = nullptr;
    std::string                rsp_str;
    bool                       ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_BDF_INFO_RSP_LEN) {
        elog("get bdf rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_BDF_INFO_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_bdf_info_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_BDF)) {
        return false;
    }
    bdf_info->bus      = rsp->bus;
    bdf_info->device   = rsp->device;
    bdf_info->function = rsp->function;
    return true;
}

bool ncsi_over_mctp_huawei::get_chip_temp(uint8_t package_id, uint8_t channel_id,
                                          uint16_t* chip_temp) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_GET_CHIP_TEMP, 0x00);

    oem_huawei_temperature_rsp_t* rsp = nullptr;
    std::string                   rsp_str;
    bool                          ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_CHIP_TEMP_RSP_LEN) {
        elog("get chip temperature rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_CHIP_TEMP_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_temperature_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_CHIP_TEMP)) {
        return false;
    }
    *chip_temp = SHORT_BY_BIG_ENDIAN(rsp->temperature);
    return true;
}

bool ncsi_over_mctp_huawei::get_om_temp(uint8_t package_id, uint8_t channel_id, uint16_t* om_temp) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_GET_OM_TEMP, 0x00);

    oem_huawei_temperature_rsp_t* rsp = nullptr;
    std::string                   rsp_str;
    bool                          ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_CHIP_TEMP_RSP_LEN) {
        elog("get om temperature rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_CHIP_TEMP_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_temperature_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_OM_TEMP)) {
        return false;
    }
    uint16_t temperature = SHORT_BY_BIG_ENDIAN(rsp->temperature);
    if (temperature == 0x7FFD) {
        elog("get om temperature(0x7FFD) is invalid");
        return false;
    }
    if (temperature == 0x7FFE) {
        elog("get om module is not available");
        return false;
    }
    if (temperature == 0x7FFF) {
        elog("read om temperature(0x7FFF) is failed");
        return false;
    }
    *om_temp = temperature;
    return true;
}

std::string get_link_width_str(uint8_t link_width) {
    switch (link_width) {
    case 0x01:
        return "x1";
    case 0x02:
        return "x2";
    case 0x04:
        return "x4";
    case 0x08:
        return "x8";
    case 0x10:
        return "x16";
    case 0x20:
        return "x32";
    case 0xFF:
        return "invalid";
    default:
        return "";
    }
}

std::string get_link_speed_str(uint8_t link_speed) {
    switch (link_speed) {
    case 0x01:
        return "Gen1 (2.5GT/s)";
    case 0x02:
        return "Gen2 (5GT/s)";
    case 0x03:
        return "Gen3 (8GT/s)";
    case 0x04:
        return "Gen4 (16GT/s)";
    case 0x05:
        return "Gen5 (32GT/s)";
    default:
        return "invalid";
    }
}

bool ncsi_over_mctp_huawei::get_link_ability(uint8_t package_id, uint8_t channel_id,
                                             ncsi_link_ability_t* link_ability) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_GET_LINK_ABILITY, 0x00);

    oem_huawei_link_ability_rsp_t* rsp = nullptr;
    std::string                    rsp_str;
    bool                           ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_LINK_ABILITY_RSP_LEN) {
        elog("get link ability rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_LINK_ABILITY_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_link_ability_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_LINK_ABILITY)) {
        return false;
    }
    link_ability->link_width = get_link_width_str(rsp->link_width);
    link_ability->link_speed = get_link_speed_str(rsp->link_speed);
    return true;
}

bool ncsi_over_mctp_huawei::get_link_info(uint8_t package_id, uint8_t channel_id,
                                          ncsi_link_info_t* link_info) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_GET_LINK_INFO, 0x00);

    oem_huawei_link_info_rsp_t* rsp = nullptr;
    std::string                 rsp_str;
    bool                        ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_LINK_INFO_RSP_LEN) {
        elog("get link info rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_LINK_INFO_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_link_info_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_LINK_INFO)) {
        return false;
    }
    link_info->link_width = get_link_width_str(rsp->link_width);
    link_info->link_speed = get_link_speed_str(rsp->link_speed);
    return true;
}

bool ncsi_over_mctp_huawei::get_dcbx_status(uint8_t package_id, uint8_t channel_id,
                                            ncsi_dcbx_status_t* dcbx_status) {
    mc::dict             ctx  = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data = COMMON_DATA(
        OEM_HUAWEI_CONTROLLER_INFO_NIC, OEM_HUAWEI_GET_DCBX_STATUS, OEM_HUAWEI_GET_DCBX_EXTRA_CMD);

    oem_huawei_dcbx_status_rsp_t* rsp = nullptr;
    std::string                   rsp_str;
    bool                          ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_DCBX_STATUS_RSP_LEN) {
        elog("get dcbx status rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_DCBX_STATUS_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_dcbx_status_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_DCBX_STATUS)) {
        return false;
    }

    // TODO：这里实现和协议定义保持一致，当前Mmctpd可能对内容做了转换，需要调试
    memcpy(dcbx_status->cos2_up, rsp->cos2_up, MCTP_NCSI_DCBX_FIELD_LEN);
    memcpy(dcbx_status->up_pgid, rsp->up_pgid, MCTP_NCSI_DCBX_FIELD_LEN);
    memcpy(dcbx_status->pgpct, rsp->pgpct, MCTP_NCSI_DCBX_FIELD_LEN);
    memcpy(dcbx_status->strict, rsp->strict, MCTP_NCSI_DCBX_FIELD_LEN);
    dcbx_status->pfcmap = rsp->pfcmap;
    return true;
}

bool ncsi_over_mctp_huawei::get_mac_addr(uint8_t package_id, uint8_t channel_id, uint8_t* mac_addr,
                                         uint8_t len) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_NIC, OEM_HUAWEI_GET_MAC_ADDR, channel_id); // TODO 如果是BroadCom，需要修改为0x00

    oem_huawei_mac_addr_rsp_t* rsp = nullptr;
    std::string                rsp_str;
    bool                       ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_MAC_ADDR_RSP_LEN) {
        elog("get mac addr rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_MAC_ADDR_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_mac_addr_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_MAC_ADDR)) {
        return false;
    }
    if (rsp->mac_addr_count == 0) {
        dlog("get mac addr count is 0");
    }
    // 存在多个mac地址，取第一个
    memcpy(mac_addr, rsp->mac_addr[0], len);
    return true;
}

bool ncsi_over_mctp_huawei::get_default_mac_addr(uint8_t package_id, uint8_t channel_id,
                                                 uint8_t* mac_addr, uint8_t len) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_NIC, OEM_HUAWEI_GET_DEFAULT_MAC_ADDR, 0x00);

    oem_huawei_mac_addr_rsp_t* rsp = nullptr;
    std::string                rsp_str;
    bool                       ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_MAC_ADDR_RSP_LEN) {
        elog("get default mac addr rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_MAC_ADDR_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_mac_addr_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_DEFAULT_MAC_ADDR)) {
        return false;
    }
    if (rsp->mac_addr_count == 0) {
        dlog("get default mac addr count is 0");
    }
    // 存在多个mac地址，取第一个
    memcpy(mac_addr, rsp->mac_addr[0], len);
    return true;
}

bool ncsi_over_mctp_huawei::get_fault_state_code(uint8_t package_id, uint8_t channel_id,
                                                 ncsi_fault_state_code_t* fault_state_code) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_GET_FAULT_STATE_CODE, 0x00);

    oem_huawei_fault_state_code_rsp_t* rsp = nullptr;
    std::string                        rsp_str;
    bool                               ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() < OEM_HUAWEI_FAULT_STATE_CODE_RSP_LEN) {
        elog("get fault state code rsp is empty or size(${rsp_size}) is less than ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_FAULT_STATE_CODE_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_fault_state_code_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_FAULT_STATE_CODE)) {
        return false;
    }
    fault_state_code->health_state = rsp->health_state;
    if (rsp->error_count == 0) {
        return true;
    }
    fault_state_code->error_count = rsp->error_count;
    // 逐个处理数组中的每个元素进行字节序转换
    for (int i = 0; i < NCSI_HUAWEI_FAULT_STATE_CODE_COUNT && i < rsp->error_count; i++) {
        fault_state_code->error_code[i] = SHORT_BY_BIG_ENDIAN(rsp->error_code[i]);
    }
    return true;
}

bool ncsi_over_mctp_huawei::enable_lldp_capability(uint8_t package_id, uint8_t channel_id) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_ENABLE_LLDP_CAPABILITY,
                    OEM_HUAWEI_ENABLE_LLDP_EXTRA_CMD);

    oem_huawei_enable_lldp_capability_rsp_t* rsp = nullptr;
    std::string                              rsp_str;
    bool                                     ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_ENABLE_LLDP_CAPABILITY_RSP_LEN) {
        elog("enable lldp capability rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_ENABLE_LLDP_CAPABILITY_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_enable_lldp_capability_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_ENABLE_LLDP_CAPABILITY)) {
        return false;
    }
    return true;
}

bool ncsi_over_mctp_huawei::enable_lldp_tx_old(uint8_t package_id, uint8_t channel_id) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_ENABLE_LLDP_TX_OLD,
                    OEM_HUAWEI_ENABLE_LLDP_TX_OLD_EXTRA_CMD);

    oem_huawei_enable_lldp_tx_old_rsp_t* rsp = nullptr;
    std::string                          rsp_str;
    bool                                 ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_ENABLE_LLDP_TX_OLD_RSP_LEN) {
        elog("enable lldp tx old rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_ENABLE_LLDP_TX_OLD_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_enable_lldp_tx_old_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_ENABLE_LLDP_TX_OLD)) {
        return false;
    }
    return true;
}

bool ncsi_over_mctp_huawei::disable_lldp_tx_old(uint8_t package_id, uint8_t channel_id) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_ENABLE_LLDP_TX_OLD,
                    OEM_HUAWEI_DISABLE_LLDP_TX_OLD_EXTRA_CMD);

    oem_huawei_enable_lldp_tx_old_rsp_t* rsp = nullptr;
    std::string                          rsp_str;
    bool                                 ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_ENABLE_LLDP_TX_OLD_RSP_LEN) {
        elog("disable lldp tx old rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_ENABLE_LLDP_TX_OLD_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_enable_lldp_tx_old_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_ENABLE_LLDP_TX_OLD)) {
        return false;
    }
    return true;
}

bool ncsi_over_mctp_huawei::disable_lldp_capability(uint8_t package_id, uint8_t channel_id) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_ENABLE_LLDP_CAPABILITY,
                    OEM_HUAWEI_DISABLE_LLDP_EXTRA_CMD);

    oem_huawei_enable_lldp_capability_rsp_t* rsp = nullptr;
    std::string                              rsp_str;
    bool                                     ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_ENABLE_LLDP_CAPABILITY_RSP_LEN) {
        elog("disable lldp capability rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_ENABLE_LLDP_CAPABILITY_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_enable_lldp_capability_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_ENABLE_LLDP_CAPABILITY)) {
        return false;
    }
    return true;
}

bool ncsi_over_mctp_huawei::get_lldp_capability(uint8_t package_id, uint8_t channel_id,
                                                uint8_t* lldp_capability) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_GET_LLDP_CAPABILITY, 0x00);

    oem_huawei_get_lldp_capability_rsp_t* rsp = nullptr;
    std::string                           rsp_str;
    bool                                  ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_GET_LLDP_CAPABILITY_RSP_LEN) {
        elog("get lldp capability rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_GET_LLDP_CAPABILITY_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_get_lldp_capability_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_LLDP_CAPABILITY)) {
        return false;
    }
    *lldp_capability = rsp->lldp_capability;
    return true;
}

bool ncsi_over_mctp_huawei::enable_lldp_tx(uint8_t package_id, uint8_t channel_id) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_ENABLE_LLDP_TX,
                    OEM_HUAWEI_ENABLE_LLDP_TX_EXTRA_CMD);

    oem_huawei_enable_lldp_tx_rsp_t* rsp = nullptr;
    std::string                      rsp_str;
    bool                             ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_ENABLE_LLDP_TX_RSP_LEN) {
        elog("enable lldp tx rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_ENABLE_LLDP_TX_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_enable_lldp_tx_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_ENABLE_LLDP_TX)) {
        return false;
    }
    return true;
}

bool ncsi_over_mctp_huawei::disable_lldp_tx(uint8_t package_id, uint8_t channel_id) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_ENABLE_LLDP_TX,
                    OEM_HUAWEI_DISABLE_LLDP_TX_EXTRA_CMD);

    oem_huawei_enable_lldp_tx_rsp_t* rsp = nullptr;
    std::string                      rsp_str;
    bool                             ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_ENABLE_LLDP_TX_RSP_LEN) {
        elog("disable lldp tx rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_ENABLE_LLDP_TX_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_enable_lldp_tx_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_ENABLE_LLDP_TX)) {
        return false;
    }
    return true;
}

bool ncsi_over_mctp_huawei::get_lldp_tx_enable(uint8_t package_id, uint8_t channel_id,
                                               uint8_t* lldp_tx_enable) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_GET_LLDP_TX_ENABLE, 0x00);

    oem_huawei_get_lldp_tx_enable_rsp_t* rsp = nullptr;
    std::string                          rsp_str;
    bool                                 ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_GET_LLDP_TX_ENABLE_RSP_LEN) {
        elog("get lldp tx enable rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_GET_LLDP_TX_ENABLE_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_get_lldp_tx_enable_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_LLDP_TX_ENABLE)) {
        return false;
    }
    *lldp_tx_enable = rsp->lldp_tx_enable;
    return true;
}

const std::string MEDIUM_TYPE_MAP[16] = {"Unknown", "Optical",   "Electric", "Copper",
                                         "Aoc",     "Interface", "Baset"};

std::string get_medium_type_str(uint8_t medium_type) {
    if (medium_type >= sizeof(MEDIUM_TYPE_MAP) / sizeof(MEDIUM_TYPE_MAP[0])) {
        return "Unknown";
    }
    return MEDIUM_TYPE_MAP[medium_type];
}

const std::map<uint8_t, std::string> CONNECTOR_TYPE_MAP = {
    {0x00, "UNDEF"},
    {0x07, "LC"},
    {0x0C, "MPO"},
    {0x0B, "OPTICAL_PIGTAIL"},
    {0x21, "COPPER_PIGTAIL"},
    {0x23, "NO_SEPARABLE_CONNECTOR"},
};

std::string get_connector_type_str(uint8_t connector_type) {
    if (CONNECTOR_TYPE_MAP.find(connector_type) != CONNECTOR_TYPE_MAP.end()) {
        return CONNECTOR_TYPE_MAP.at(connector_type);
    }
    return "";
}

float covert_micro_to_milli(uint16_t value) {
    if (value == 0xFFFF) {
        return 0xFFFF;
    }

    return value / 1000.0;
}

const std::map<uint8_t, std::string> IDENTIFIER_MAP = {
    {0x00, "UNDEF"}, {0x01, "GBIC"},      {0x03, "SFP_SFP_PLUS_SFP28"},
    {0x0c, "QSFP"},  {0x0d, "QSFP_PLUS"}, {0x11, "QSFP28"},
};

std::string get_identifier_str(uint8_t identifier) {
    if (IDENTIFIER_MAP.find(identifier) != IDENTIFIER_MAP.end()) {
        return IDENTIFIER_MAP.at(identifier);
    }
    return "";
}

bool ncsi_over_mctp_huawei::get_optical_module_info(
    uint8_t package_id, uint8_t channel_id, ncsi_optical_module_info_t* optical_module_info) {
    mc::dict             ctx = get_oem_command_ctx(package_id, channel_id);
    std::vector<uint8_t> data =
        COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, OEM_HUAWEI_GET_OPTICAL_MODULE_INFO, 0x00);

    oem_huawei_optical_module_info_rsp_t* rsp = nullptr;
    std::string                           rsp_str;
    bool                                  ret = request(ctx, data, rsp_str);
    if (!ret) {
        return false;
    }
    if (rsp_str.empty() || rsp_str.size() != OEM_HUAWEI_OPTICAL_MODULE_INFO_RSP_LEN) {
        elog("get optical module info rsp is empty or size(${rsp_size}) is not equal to ${size}",
             ("rsp_size", rsp_str.size())("size", OEM_HUAWEI_OPTICAL_MODULE_INFO_RSP_LEN));
        return false;
    }
    rsp = reinterpret_cast<oem_huawei_optical_module_info_rsp_t*>(rsp_str.data());
    if (!check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(rsp),
                             OEM_HUAWEI_GET_OPTICAL_MODULE_INFO)) {
        return false;
    }
    memcpy(optical_module_info->part_number, rsp->part_number, MCTP_NCSI_OPTICAL_MODULE_FIELD_LEN);
    memcpy(optical_module_info->manufacturer, rsp->vendor, MCTP_NCSI_OPTICAL_MODULE_FIELD_LEN);
    memcpy(optical_module_info->serial_number, rsp->serial_number,
           MCTP_NCSI_OPTICAL_MODULE_FIELD_LEN);
    optical_module_info->medium_type               = get_medium_type_str(rsp->type);
    optical_module_info->connector_type            = get_connector_type_str(rsp->connector);
    optical_module_info->transceiver_distance      = mc::ntoh(rsp->transceiver_distance);  // 大端序解析
    optical_module_info->rx_loss_status            = (rsp->rx_los_state == 0x01);
    optical_module_info->tx_fault_status           = (rsp->tx_full_state == 0x01);
    optical_module_info->tx_bias_current_milliamps = covert_micro_to_milli(mc::ntoh(rsp->working_tx_bias));
    optical_module_info->tx_power_milliwatts       = covert_micro_to_milli(mc::ntoh(rsp->working_tx_power));
    optical_module_info->rx_power_milliwatts       = covert_micro_to_milli(mc::ntoh(rsp->working_rx_power));
    optical_module_info->supply_voltage = covert_micro_to_milli(mc::ntoh(rsp->working_voltage));
    optical_module_info->tx_power_upper_threshold_critical =
        covert_micro_to_milli(mc::ntoh(rsp->alarm_tx_power_threshold));
    optical_module_info->rx_power_upper_threshold_critical =
        covert_micro_to_milli(mc::ntoh(rsp->alarm_rx_power_threshold));
    if (optical_module_info->medium_type == "Optical" ||
        optical_module_info->medium_type == "Aoc" ||
        optical_module_info->medium_type == "Electric") {
        optical_module_info->wave_length_nanometer = std::to_string(mc::ntoh(rsp->wave_length)) + "nm";
        optical_module_info->identifier            = get_identifier_str(rsp->identifier);
        optical_module_info->temperature_lower_threshold_critical = mc::ntoh(rsp->alarm_low_temp_threshold);
        optical_module_info->temperature_upper_threshold_critical = mc::ntoh(rsp->alarm_high_temp_threshold);
    }
    return true;
}

// 日志类型配置数组
static const std::vector<ncsi_log_type_config_t> LOG_TYPE_CONFIGS = {
    {0, 640, 0,       "mpu_ucode"},   // MPU微码日志
    {0, 256, 0xA0000, "last_word"},   // 临终遗言日志  
    {1, 136, 0,       "counter"},     // 计数器日志
    {2, 8,   0,       "register"}     // 寄存器日志
};

bool ncsi_over_mctp_huawei::collect_logs_by_ncsi(uint8_t package_id, 
                                                 std::vector<std::string>& log_files, 
                                                 const std::string& log_dir) {
    bool is_success = true;
    
    for (const auto& config : LOG_TYPE_CONFIGS) {
        // 生成文件名: 类型_时间戳.bin
        auto now = std::time(nullptr);
        struct tm tm_buf;
        struct tm* tm = localtime_r(&now, &tm_buf);  // 线程安全版本
        if (!tm) {
            elog("localtime_r failed for log file timestamp");
            is_success = false;
            continue;
        }
        char timestamp[16];  // "%Y%m%d%H%M%S" = 14字符 + null终止符
        strftime(timestamp, sizeof(timestamp), "%Y%m%d%H%M%S", tm);
        std::string filename = config.name_prefix + "_" + timestamp + ".bin";
        log_files.push_back(filename);
        
        // 获取日志数据
        std::string log_data;
        bool ret = get_log_by_ncsi_internal(package_id, config.log_type, 
                                           config.total_frame, config.base_offset, log_data);
        
        if (!ret) {
            is_success = false;
            elog("cannot fetch ${log_type} log", ("log_type", config.name_prefix));
            continue;
        }
        
        // 写入文件 - 使用mc::filesystem
        std::string full_path = mc::filesystem::join(log_dir, filename).string();
        if (!mc::filesystem::write_file(full_path, log_data)) {
            elog("write file failed: ${file}", ("file", full_path));
            is_success = false;
            continue;
        }
        
        // 设置文件权限 (0640)
        if (chmod(full_path.c_str(), S_IRUSR | S_IWUSR | S_IRGRP) != 0) {
            elog("chmod failed for file: ${file}", ("file", full_path));
            // 权限设置失败不影响整体成功状态，只记录错误
        }
        
        ilog("finish to collect ${log_type} log", ("log_type", config.name_prefix));
    }
    
    return is_success;
}

bool ncsi_over_mctp_huawei::get_log_by_ncsi_internal(uint8_t package_id, uint32_t log_type, 
                                                     uint32_t total_frame, uint32_t base_offset, 
                                                     std::string& log_data) {
    log_data.clear();
    uint8_t sub_cmd = OEM_HUAWEI_GET_LOG;  // 优先使用新固件命令
    uint32_t frame_index = 1;
    
    // 分帧读取日志数据
    while (frame_index <= total_frame) {
        oem_huawei_get_log_req_t log_request;
        log_request.offset = base_offset + OEM_HUAWEI_LOG_FRAME_SIZE * (frame_index - 1);
        log_request.length = OEM_HUAWEI_LOG_FRAME_SIZE;
        
        std::string response;
        bool ret = send_get_log_command(package_id, sub_cmd, log_type, log_request, response);
        
        // 检查发送结果和响应数据
        if (!ret || !check_respense_data(reinterpret_cast<oem_huawei_public_rsp_t*>(response.data()), sub_cmd)) {
            if (sub_cmd == OEM_HUAWEI_GET_LOG_OLD) {
                // 旧命令也失败，返回错误
                elog("get log by ncsi failed");
                return false;
            }
            // 新命令失败，切换到旧命令并重试
            nlog("get log by ncsi failed, switching to old command");
            sub_cmd = OEM_HUAWEI_GET_LOG_OLD;
            log_data.clear();
            frame_index = 1;
            continue;
        }
        
        // 日志返回的数据大小不一致，只需要包含响应头即可
        if (response.size() >= sizeof(oem_huawei_public_rsp_t)) {
            oem_huawei_get_log_rsp_t* log_rsp = nullptr;
            log_rsp = reinterpret_cast<oem_huawei_get_log_rsp_t*>(response.data());
            size_t actual_data_size = response.size() - sizeof(oem_huawei_public_rsp_t) - 7; // 减去7字节保留字段
            if (actual_data_size > 0) {
                log_data.append(reinterpret_cast<const char*>(log_rsp->data), actual_data_size);
            }
        } else {
            elog("get log by ncsi failed, response size ${size} is invalid", ("size", response.size()));
            return false;
        }
        
        frame_index++;
    }
    
    return true;
}

bool ncsi_over_mctp_huawei::send_get_log_command(uint8_t package_id, uint8_t sub_cmd_id, 
                                                 uint32_t log_type, 
                                                 const oem_huawei_get_log_req_t& log_request, 
                                                 std::string& response) {
    // 构造命令上下文
    mc::dict ctx = get_oem_command_ctx(package_id, 0);  // channel_id = 0
    
    // 构造OEM命令数据
    std::vector<uint8_t> data = COMMON_DATA(OEM_HUAWEI_CONTROLLER_INFO_PUBLIC, sub_cmd_id, 
                                           static_cast<uint8_t>(log_type));
    
    // 添加请求数据 (offset + length)
    const uint8_t* req_data = reinterpret_cast<const uint8_t*>(&log_request);
    data.insert(data.end(), req_data, req_data + sizeof(log_request));
    
    // 发送NCSI命令
    return request(ctx, data, response);
}

} // namespace dev
